﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Mime;
using Ardalis.ListStartupServices;
using BlazorAdmin;
using BlazorAdmin.Services;
using Blazored.LocalStorage;
using BlazorShared;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;

namespace Microsoft.eShopWeb.Web;

public class Startup
{
    private IServiceCollection _services;

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureDevelopmentServices(IServiceCollection services)
    {
        // use in-memory database
        ConfigureInMemoryDatabases(services);

        // use real database
        //ConfigureProductionServices(services);
    }

    public void ConfigureDockerServices(IServiceCollection services)
    {
        services.AddDataProtection()
            .SetApplicationName("eshopwebmvc")
            .PersistKeysToFileSystem(new DirectoryInfo(@"./"));

        ConfigureDevelopmentServices(services);
    }

    private void ConfigureInMemoryDatabases(IServiceCollection services)
    {
        // use in-memory database
        services.AddDbContext<CatalogContext>(c =>
            c.UseInMemoryDatabase("Catalog"));

        // Add Identity DbContext
        services.AddDbContext<AppIdentityDbContext>(options =>
            options.UseInMemoryDatabase("Identity"));

        ConfigureServices(services);
    }

    public void ConfigureProductionServices(IServiceCollection services)
    {
        // use real database
        // Requires LocalDB which can be installed with SQL Server Express 2016
        // https://www.microsoft.com/en-us/download/details.aspx?id=54284
        services.AddDbContext<CatalogContext>(c =>
            c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection")));

        // Add Identity DbContext
        services.AddDbContext<AppIdentityDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));

        ConfigureServices(services);
    }

    public void ConfigureTestingServices(IServiceCollection services)
    {
        ConfigureInMemoryDatabases(services);
    }


    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCookieSettings();


        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
            {
                options.Cookie.HttpOnly = true;
                options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
                options.Cookie.SameSite = SameSiteMode.Lax;
            });

        services.AddIdentity<ApplicationUser, IdentityRole>()
                   .AddDefaultUI()
                   .AddEntityFrameworkStores<AppIdentityDbContext>()
                                   .AddDefaultTokenProviders();

        services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();

        services.AddCoreServices(Configuration);
        services.AddWebServices(Configuration);

        // Add memory cache services
        services.AddMemoryCache();
        services.AddRouting(options =>
        {
                // Replace the type and the name used to refer to it with your own
                // IOutboundParameterTransformer implementation
                options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
        });
        services.AddMvc(options =>
        {
            options.Conventions.Add(new RouteTokenTransformerConvention(
                     new SlugifyParameterTransformer()));

        });
        services.AddControllersWithViews();
        services.AddRazorPages(options =>
        {
            options.Conventions.AuthorizePage("/Basket/Checkout");
        });
        services.AddHttpContextAccessor();
        services.AddHealthChecks();
        services.Configure<ServiceConfig>(config =>
        {
            config.Services = new List<ServiceDescriptor>(services);

            config.Path = "/allservices";
        });


        var baseUrlConfig = new BaseUrlConfiguration();
        Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
        services.AddScoped<BaseUrlConfiguration>(sp => baseUrlConfig);
        // Blazor Admin Required Services for Prerendering
        services.AddScoped<HttpClient>(s => new HttpClient
        {
            BaseAddress = new Uri(baseUrlConfig.WebBase)
        });

        // add blazor services
        services.AddBlazoredLocalStorage();
        services.AddServerSideBlazor();


        services.AddScoped<ToastService>();
        services.AddScoped<HttpService>();
        services.AddBlazorServices();

        services.AddDatabaseDeveloperPageExceptionFilter();

        _services = services; // used to debug registered services
    }


    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        var catalogBaseUrl = Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string;
        if (!string.IsNullOrEmpty(catalogBaseUrl))
        {
            app.Use((context, next) =>
            {
                context.Request.PathBase = new PathString(catalogBaseUrl);
                return next();
            });
        }

        app.UseHealthChecks("/health",
            new HealthCheckOptions
            {
                ResponseWriter = async (context, report) =>
                {
                    var result = new
                    {
                        status = report.Status.ToString(),
                        errors = report.Entries.Select(e => new
                        {
                            key = e.Key,
                            value = Enum.GetName(typeof(HealthStatus), e.Value.Status)
                        })
                    }.ToJson();
                    context.Response.ContentType = MediaTypeNames.Application.Json;
                    await context.Response.WriteAsync(result);
                }
            });
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseShowAllServicesMiddleware();
            app.UseMigrationsEndPoint();
            app.UseWebAssemblyDebugging();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseBlazorFrameworkFiles();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCookiePolicy();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
            endpoints.MapRazorPages();
            endpoints.MapHealthChecks("home_page_health_check");
            endpoints.MapHealthChecks("api_health_check");
                //endpoints.MapBlazorHub("/admin");
                endpoints.MapFallbackToFile("index.html");
        });
    }

}
